Conversation
…/contract_fix Please enter a commit message to explain why this merge is necessary,
Walkthrough계약 적법성 워크플로가 다수의 엔드포인트로 확장되었습니다. 특약 저장/갱신, 소유자·구매자 적법성 요청 생성·삭제·거절 흐름과 Redis 기반 임시저장, IdentityVerification VO 통합으로 ContractDTO 매핑이 변경되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant Controller as ContractController
participant Service as ContractService
participant ID as IdentityVerificationService
participant Redis as RedisStore
participant AI as AIServer
Client->>Controller: GET /getContract (contractChatId)
Controller->>Service: getContract(contractChatId, userId)
Service->>ID: fetchDecrypted(ownerContractId, buyerContractId)
ID-->>Service: ownerVO, buyerVO
Service-->>Controller: ContractDTO(ownerVO, buyerVO 포함)
Controller-->>Client: ApiResponse<ContractDTO>
Client->>Controller: POST /suggest/legality (owner submits UpdateLegalityDTO)
Controller->>Service: updateOwnerLegality(contractChatId, userId, dto)
Service->>Redis: set(final-contract:legality:<chatId>:<ownerId>, JSON)
Service->>AI: AiMessage(알림)
Controller-->>Client: 200
Client->>Controller: POST /update/legality (buyer applies SpecialContractUpdateDTO)
Controller->>Service: updateBuyerLegality(contractChatId, userId, dto)
Service->>Redis: get(...) -> LegalityRequestDTO
Service->>Service: updateSpecialContract(...) (persist)
Service->>Redis: delete(...)
Service->>AI: AiMessage(완료 알림)
Controller-->>Client: 200
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Assessment against linked issues
Assessment against linked issues: Out-of-scope changes
Possibly related PRs
Suggested reviewers
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
Fixed
Show fixed
Hide fixed
There was a problem hiding this comment.
Actionable comments posted: 15
🔭 Outside diff range comments (1)
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (1)
394-452: PII 외부 전송 리스크: 최소 전송/마스킹/로깅 축소 필요복호화된 신원정보(owner/buyer VO)가 포함된
ContractDTO전체를 외부 AI 서버로 전송하고 있습니다. 이름/주민번호/전화/주소 등 민감 정보가 포함될 수 있으며, 개인정보보호 및 내부 보안 규정에 저촉될 수 있습니다.권장 조치:
- AI 서버에 필요한 필드만 포함한 전용 DTO(예: LegalityValidationRequest)를 정의하고 민감 필드는 마스킹(예: 이름 이니셜/전화 일부/주소 시군구 단위) 또는 제거.
- 응답/요청 본문 로깅 최소화(현재 warn 레벨로 DTO 전체 문자열화). 민감 데이터는 로깅 금지 또는 요약 로그로 대체.
- 외부 호출 타임아웃/재시도/서킷브레이커 구성(RestTemplate 설정)을 통해 장애 고립.
- 전송 채널 TLS 보장 및 대상 도메인 화이트리스트 관리.
원하시면 전용 DTO와 매핑/마스킹 유틸, RestTemplate 커스터마이저(Timeout) 코드를 함께 제공하겠습니다.
🧹 Nitpick comments (8)
src/main/java/org/scoula/domain/contract/dto/LegalityRequestDTO.java (1)
13-13: 날짜 필드 타입을 LocalDateTime으로 변경 고려
createdAt필드가 String 타입으로 정의되어 있는데, 날짜/시간 데이터는LocalDateTime또는LocalDate타입을 사용하는 것이 타입 안정성과 날짜 연산에 유리합니다.+import java.time.LocalDateTime; +import com.fasterxml.jackson.annotation.JsonFormat; +import org.scoula.global.common.constant.Constants; + @Getter @Setter @NoArgsConstructor @AllArgsConstructor @Builder public class LegalityRequestDTO { private String legalBasis; private Long requestId; - private String createdAt; + @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = Constants.DateTime.DEFAULT_DATETIME_FORMAT) + private LocalDateTime createdAt; }src/main/java/org/scoula/domain/contract/service/ContractService.java (1)
82-83: JavaDoc 주석 형식 수정 필요JavaDoc 주석이 한 줄로 압축되어 있어 가독성이 떨어집니다. 표준 형식으로 수정이 필요합니다.
- /** * step4 start 특약을 개약 테이블에 저장하기 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 */ + /** + * step4 start 특약을 계약 테이블에 저장하기 + * + * @param contractChatId 채팅방 아이디 + * @param userId 유저 아이디 + */src/main/java/org/scoula/domain/contract/controller/ContractController.java (1)
111-116: 주석 제거 및 명확한 문서화 필요"메서드만 만들어서 써도 될 듯"이라는 불필요한 주석이 포함되어 있습니다. 프로덕션 코드에서는 이런 임시 주석을 제거해야 합니다.
- // 메서드만 만들어서 써도 될 듯 @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경")src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java (3)
103-111: HTTP 메서드/경로 네이밍 개선 제안: 조회는 GET + 리소스형 경로 사용조회 목적 엔드포인트임에도 POST를 사용하고 있고, 경로명이 동사형(
getContracts)입니다. REST 관점에서GET /contracts또는GET /contract로의 정리가 바람직합니다. 기존/getContract와의 중복도 고려해 하나만 유지하는 것을 권장합니다.적용 제안(diff):
- @PostMapping("/getContracts") + @GetMapping("/contract") public ResponseEntity<ApiResponse<ContractDTO>> getContracts(참고: 기존
/getContract엔드포인트와의 중복을 해소하려면 하나를 제거하거나 둘 중 하나를 대체하도록 리팩터링이 필요합니다. 원하시면 리소스 전체 경로/메서드 체계를 일괄 제안드리겠습니다.
132-139: 삭제 엔드포인트 경로 정리
/delete/legality는 동사형 경로입니다.DELETE /legality로 단순화하는 것이 RESTful합니다. (반환 타입은 String 유지 가능하나, 가능하면 표준화된 payload를 쓰길 권장)적용 제안(diff):
- @DeleteMapping("/delete/legality") + @DeleteMapping("/legality") public ResponseEntity<ApiResponse<String>> deleteOwnerLegality(
103-111: Swagger 문서화 어노테이션 누락가이드라인에 따라 컨트롤러 엔드포인트에 Swagger(OpenAPI) 어노테이션을 추가하세요. 최소한
@Operation(summary=..., description=...)를 각 메서드에 부여하십시오.예시(추가 코드):
import io.swagger.v3.oas.annotations.Operation; @Operation(summary = "계약 조회", description = "특약 저장 이후 최신 계약 정보를 조회합니다.") @GetMapping("/contract") public ResponseEntity<ApiResponse<ContractDTO>> getContracts(...) { ... } @Operation(summary = "특약 저장", description = "현재 계약의 특약 사항을 MongoDB에 저장합니다.") @PostMapping("/specialContract") public ResponseEntity<ApiResponse<Void>> saveSpecialContract(...) { ... } @Operation(summary = "임대인 적법성 수정 요청 삭제", description = "임대인이 보낸 적법성 수정 요청을 삭제합니다.") @DeleteMapping("/legality") public ResponseEntity<ApiResponse<String>> deleteOwnerLegality(...) { ... } // 나머지 엔드포인트에도 동일 패턴으로 추가Also applies to: 112-120, 132-139, 141-150, 152-161, 163-171
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (2)
48-49: ObjectMapper 필드 주입: 일관되게 재사용하도록 리팩터 필요클래스 필드에
objectMapper가 추가되었지만, 메서드 내부에서 새로운 ObjectMapper를 반복 생성하는 구간이 남아 있습니다(예: Line 266-267, 316-317). 필드 인스턴스를 재사용하도록 정리하세요.적용 제안(변경이 필요한 외부 라인 참고):
// 예: Line 266-267 // ObjectMapper objectMapper = new ObjectMapper(); // String json = objectMapper.writeValueAsString(dto); String json = this.objectMapper.writeValueAsString(dto); // 예: Line 316-317 // ObjectMapper objectMapper = new ObjectMapper(); // PaymentDTO dto = objectMapper.readValue(json, PaymentDTO.class); PaymentDTO dto = this.objectMapper.readValue(json, PaymentDTO.class);
421-447: AI 응답 로깅 레벨/내용 최소화 제안현재 warn 레벨로 응답 바디 전체를 로그합니다. 운영 환경에서는 민감/불필요 데이터가 포함될 수 있으므로, 상태코드와 요약 필드만 debug 레벨로 기록하거나 비활성화하는 것을 권장합니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
src/main/java/org/scoula/domain/contract/controller/ContractController.java(1 hunks)src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java(2 hunks)src/main/java/org/scoula/domain/contract/dto/ContractDTO.java(2 hunks)src/main/java/org/scoula/domain/contract/dto/LegalityRequestDTO.java(1 hunks)src/main/java/org/scoula/domain/contract/dto/UpdateLegalityDTO.java(1 hunks)src/main/java/org/scoula/domain/contract/service/ContractService.java(2 hunks)src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java(7 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/main/java/org/scoula/**/controller/*Controller{,Impl}.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
Use interface-implementation pattern for controllers:
*Controller.java(interface) and*ControllerImpl.java(implementation)
Files:
src/main/java/org/scoula/domain/contract/controller/ContractController.javasrc/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
src/main/java/org/scoula/**/controller/**/*.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
src/main/java/org/scoula/**/controller/**/*.java: All controller endpoints should return a consistentApiResponse<T>wrapper
Add Swagger annotations to controller endpoints for API documentation
Files:
src/main/java/org/scoula/domain/contract/controller/ContractController.javasrc/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
src/main/java/org/scoula/**/service/*Service{Interface,Impl}.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
Use interface-implementation pattern for services:
*ServiceInterface.java(interface) and*ServiceImpl.java(implementation)
Files:
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
🧬 Code Graph Analysis (3)
src/main/java/org/scoula/domain/contract/dto/UpdateLegalityDTO.java (1)
src/main/java/org/scoula/domain/contract/dto/LegalityRequestDTO.java (1)
Getter(5-14)
src/main/java/org/scoula/domain/contract/dto/LegalityRequestDTO.java (1)
src/main/java/org/scoula/domain/contract/dto/UpdateLegalityDTO.java (1)
Getter(5-12)
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (1)
src/main/java/org/scoula/domain/precontract/service/OwnerPreContractServiceImpl.java (1)
Service(50-752)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Test Coverage
- GitHub Check: CodeQL Security Analysis (java)
🔇 Additional comments (6)
src/main/java/org/scoula/domain/contract/dto/UpdateLegalityDTO.java (1)
1-12: DTO 클래스가 적절하게 구성되었습니다Lombok 어노테이션을 활용하여 보일러플레이트 코드를 효과적으로 줄였으며, 적법성 업데이트에 필요한 필드를 간결하게 정의했습니다.
src/main/java/org/scoula/domain/contract/dto/ContractDTO.java (1)
72-72: contractChatId 소스 변경 검증 완료 — 문제 없음ownerVO.getContractId()는 identity_verification.contract_id에 매핑되며, 매퍼(삽입/조회)에서 contractChatId로 전달/조회되므로 document.getContractChatId()와 동일한 값입니다.
- src/main/java/org/scoula/domain/contract/dto/ContractDTO.java: .contractChatId(ownerVO.getContractId())
- src/main/java/org/scoula/domain/precontract/vo/IdentityVerificationInfoVO.java: private Long contractId
- src/main/resources/org/scoula/domain/precontract/mapper/OwnerPreContractMapper.xml: insertIdentityVerification에서 contract_id에 #{contractChatId} 삽입, selectIdentityVerificationInfo에서 contract_id를 조회
- src/main/java/org/scoula/domain/contract/document/ContractMongoDocument.java: private Long contractChatId 필드 정의
src/main/java/org/scoula/domain/contract/controller/ContractController.java (1)
15-15: 구현체 확인 — ContractControllerImpl.java가 존재합니다.ContractController 인터페이스의 구현체가 저장소에서 확인되었습니다. 구현체 파일 경로:
- src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (3)
115-123: 계약 조회 시 실명인증 정보(소유자/임차인) 통합 주입 LGTM
IdentityVerificationInfoVO를 통해 계약 DTO 구성 시 주소/연락처 등 신원확인 정보를 병합하는 변경은 의도와 일치하며 문제없습니다.
382-393: 특약 저장 로직 LGTM예외 래핑과 로깅 처리 적절합니다. 반환을 Void로 통일한 것도 컨트롤러 ApiResponse와 일치합니다.
605-616: 작성 완료 알림 LGTM검증 호출과 사용자 알림 흐름이 간단하고 일관적입니다.
| public interface ContractController { | ||
|
|
||
| // step 1 (init) | ||
| @ApiOperation(value = "[계약전_임차인] 계약서를 몽고DB에 저장", notes = "계약서에 필요한 항목들을 가져와서 몽고 DB에 계약서 만들기") | ||
| // 대기 | ||
| @ApiOperation( | ||
| value = "[대기] 계약전_임차인 | 계약서를 몽고DB에 저장", | ||
| notes = "계약서에 필요한 항목들을 가져와서 몽고 DB에 계약서 만들기") | ||
| ResponseEntity<ApiResponse<Void>> saveContractMongo( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| // step 1 : start | ||
| @ApiOperation(value = "[계약서 _ 정보 조회 1] 계약서 전체 조회", notes = "계약서 가져오기") | ||
| // 정보 조회 | ||
| @ApiOperation(value = "[정보 조회] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | ||
| ResponseEntity<ApiResponse<ContractDTO>> getContract( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[채팅 _ 정보 조회 1] 정보 조회 시작", notes = "정보조회 마지막 단계에서 다음 단계로 넘어가기 Message") | ||
| @ApiOperation(value = "[정보 조회] 채팅 1 | 정보 조회 시작", notes = "정보조회 마지막 단계에서 다음 단계로 넘어가기 Message") | ||
| ResponseEntity<ApiResponse<Void>> getContractNext( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| // step 1 : finish | ||
| @ApiOperation( | ||
| value = "[채팅 _ 정보 조회 2] 정보 조회에서 다음단계로 가기", | ||
| value = "[정보 조회] 채팅 2 | 정보 조회에서 다음단계로 가기", | ||
| notes = "다음 단계 여부(true/false)를 받아서 다음 단계로 넘어가기") | ||
| ResponseEntity<ApiResponse<Boolean>> nextStep( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestBody NextStepDTO dto); | ||
|
|
||
| @ApiOperation(value = "[채팅 _ 금액 조회 1]", notes = "금액을 조율하기 위해 금액을 조회") | ||
| // 금액 조정 | ||
| @ApiOperation(value = "[금액 조정] 채팅 1 | 금액 조회", notes = "금액을 조율하기 위해 금액을 조회") | ||
| ResponseEntity<ApiResponse<PaymentDTO>> getDepositPrice( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[채팅 _ 금액 요청 2]", notes = "임대인이 금액을 요청") | ||
| @ApiOperation(value = "[금액 조정] 채팅 2 | 금액 요청 ", notes = "임대인이 금액을 요청") | ||
| ResponseEntity<ApiResponse<Void>> saveDepositPrice( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestBody PaymentDTO dto); | ||
|
|
||
| @ApiOperation(value = "[채팅 _ 금액 거절 3]", notes = "임차인이 금액을 거절") | ||
| @ApiOperation(value = "[금액 조정] 채팅 3 | 금액 거절", notes = "임차인이 금액을 거절") | ||
| ResponseEntity<ApiResponse<Void>> deleteDepositPrice( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[채팅 _ 금액 수락 4]", notes = "임대인과 임차인 모두 동의") | ||
| @ApiOperation(value = "[금액 조정] 채팅 4 | 금액 수락", notes = "임대인과 임차인 모두 동의") | ||
| ResponseEntity<ApiResponse<Void>> updateDepositPrice( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "AI 적법성 확인 from 몽고DB", notes = "몽고DB에 있는 계약서를 AI로 보내고, 적법성 받기") | ||
| ResponseEntity<ApiResponse<LegalityDTO>> getLegality( | ||
| // 적법성 검사 | ||
| @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | ||
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "특약을 계약서 DB에 저장 FROM 몽고DB", notes = "특약 테이블에 있는걸 계약서로 가져오기") | ||
| @ApiOperation(value = "[적법성 검사] 계약서 1 몽고DB만 있는 것 ", notes = "몽고DB에 특약 저장하기 ") | ||
| ResponseEntity<ApiResponse<Void>> saveSpecialContract( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경") | ||
| ResponseEntity<ApiResponse<Void>> updateSpecialContract( | ||
| @ApiOperation( | ||
| value = "[적법성 검사] 채팅 1 | AI 적법성 확인 from 몽고DB ", | ||
| notes = "몽고DB에 있는 계약서를 AI로 보내고, 적법성 받기") | ||
| ResponseEntity<ApiResponse<LegalityDTO>> getLegality( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[적법성 검사] 채팅 2 | 임대인 삭제", notes = "임대인 : 적법성 검사 삭제") | ||
| ResponseEntity<ApiResponse<String>> deleteOwnerLegality( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[적법성 검사] 채팅 3 | 임대인 수정", notes = "임대인 : 적법성 검사 수정") | ||
| ResponseEntity<ApiResponse<Void>> updateOwnerLegality( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestBody UpdateLegalityDTO dto); | ||
|
|
||
| @ApiOperation(value = "[적법성 검사] 채팅 4 | 임차인 수정", notes = "임차인 : 적법성 검사 수정 완료") | ||
| ResponseEntity<ApiResponse<Void>> updateBuyerLegality( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestBody SpecialContractUpdateDTO dto); | ||
|
|
||
| @ApiOperation(value = "적법성 검사 후 다음단계로 넘어가기", notes = "적법성 검사 후 AI 메세지를 보낸다") | ||
| @ApiOperation(value = "[적법성 검사] 채팅 5 | 임차인 거절", notes = "임차인 : 적법성 검사 거절") | ||
| ResponseEntity<ApiResponse<String>> rejectBuyerLegality( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") | ||
| ResponseEntity<ApiResponse<Void>> sendStep4( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| // 메서드만 만들어서 써도 될 듯 | ||
| @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경") | ||
| ResponseEntity<ApiResponse<Void>> updateSpecialContract( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails, | ||
| @RequestBody SpecialContractUpdateDTO dto); |
There was a problem hiding this comment.
🛠️ Refactor suggestion
Swagger 어노테이션 확인 필요
코딩 가이드라인에 따르면 모든 컨트롤러 엔드포인트에 Swagger 어노테이션을 추가해야 합니다. 현재 @ApiOperation은 있지만 파라미터에 대한 @ApiParam 어노테이션이 누락되어 있습니다.
예시로 하나의 메서드에 대한 수정 방안입니다:
+import io.swagger.annotations.ApiParam;
ResponseEntity<ApiResponse<Void>> updateOwnerLegality(
- @PathVariable Long contractChatId,
+ @ApiParam(value = "채팅방 ID", required = true) @PathVariable Long contractChatId,
@AuthenticationPrincipal CustomUserDetails userDetails,
- @RequestBody UpdateLegalityDTO dto);
+ @ApiParam(value = "적법성 업데이트 정보", required = true) @RequestBody UpdateLegalityDTO dto);모든 메서드의 파라미터에 동일하게 적용하시기 바랍니다.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public interface ContractController { | |
| // step 1 (init) | |
| @ApiOperation(value = "[계약전_임차인] 계약서를 몽고DB에 저장", notes = "계약서에 필요한 항목들을 가져와서 몽고 DB에 계약서 만들기") | |
| // 대기 | |
| @ApiOperation( | |
| value = "[대기] 계약전_임차인 | 계약서를 몽고DB에 저장", | |
| notes = "계약서에 필요한 항목들을 가져와서 몽고 DB에 계약서 만들기") | |
| ResponseEntity<ApiResponse<Void>> saveContractMongo( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // step 1 : start | |
| @ApiOperation(value = "[계약서 _ 정보 조회 1] 계약서 전체 조회", notes = "계약서 가져오기") | |
| // 정보 조회 | |
| @ApiOperation(value = "[정보 조회] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[채팅 _ 정보 조회 1] 정보 조회 시작", notes = "정보조회 마지막 단계에서 다음 단계로 넘어가기 Message") | |
| @ApiOperation(value = "[정보 조회] 채팅 1 | 정보 조회 시작", notes = "정보조회 마지막 단계에서 다음 단계로 넘어가기 Message") | |
| ResponseEntity<ApiResponse<Void>> getContractNext( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // step 1 : finish | |
| @ApiOperation( | |
| value = "[채팅 _ 정보 조회 2] 정보 조회에서 다음단계로 가기", | |
| value = "[정보 조회] 채팅 2 | 정보 조회에서 다음단계로 가기", | |
| notes = "다음 단계 여부(true/false)를 받아서 다음 단계로 넘어가기") | |
| ResponseEntity<ApiResponse<Boolean>> nextStep( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody NextStepDTO dto); | |
| @ApiOperation(value = "[채팅 _ 금액 조회 1]", notes = "금액을 조율하기 위해 금액을 조회") | |
| // 금액 조정 | |
| @ApiOperation(value = "[금액 조정] 채팅 1 | 금액 조회", notes = "금액을 조율하기 위해 금액을 조회") | |
| ResponseEntity<ApiResponse<PaymentDTO>> getDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[채팅 _ 금액 요청 2]", notes = "임대인이 금액을 요청") | |
| @ApiOperation(value = "[금액 조정] 채팅 2 | 금액 요청 ", notes = "임대인이 금액을 요청") | |
| ResponseEntity<ApiResponse<Void>> saveDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody PaymentDTO dto); | |
| @ApiOperation(value = "[채팅 _ 금액 거절 3]", notes = "임차인이 금액을 거절") | |
| @ApiOperation(value = "[금액 조정] 채팅 3 | 금액 거절", notes = "임차인이 금액을 거절") | |
| ResponseEntity<ApiResponse<Void>> deleteDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[채팅 _ 금액 수락 4]", notes = "임대인과 임차인 모두 동의") | |
| @ApiOperation(value = "[금액 조정] 채팅 4 | 금액 수락", notes = "임대인과 임차인 모두 동의") | |
| ResponseEntity<ApiResponse<Void>> updateDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "AI 적법성 확인 from 몽고DB", notes = "몽고DB에 있는 계약서를 AI로 보내고, 적법성 받기") | |
| ResponseEntity<ApiResponse<LegalityDTO>> getLegality( | |
| // 적법성 검사 | |
| @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "특약을 계약서 DB에 저장 FROM 몽고DB", notes = "특약 테이블에 있는걸 계약서로 가져오기") | |
| @ApiOperation(value = "[적법성 검사] 계약서 1 몽고DB만 있는 것 ", notes = "몽고DB에 특약 저장하기 ") | |
| ResponseEntity<ApiResponse<Void>> saveSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경") | |
| ResponseEntity<ApiResponse<Void>> updateSpecialContract( | |
| @ApiOperation( | |
| value = "[적법성 검사] 채팅 1 | AI 적법성 확인 from 몽고DB ", | |
| notes = "몽고DB에 있는 계약서를 AI로 보내고, 적법성 받기") | |
| ResponseEntity<ApiResponse<LegalityDTO>> getLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 채팅 2 | 임대인 삭제", notes = "임대인 : 적법성 검사 삭제") | |
| ResponseEntity<ApiResponse<String>> deleteOwnerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 채팅 3 | 임대인 수정", notes = "임대인 : 적법성 검사 수정") | |
| ResponseEntity<ApiResponse<Void>> updateOwnerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody UpdateLegalityDTO dto); | |
| @ApiOperation(value = "[적법성 검사] 채팅 4 | 임차인 수정", notes = "임차인 : 적법성 검사 수정 완료") | |
| ResponseEntity<ApiResponse<Void>> updateBuyerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody SpecialContractUpdateDTO dto); | |
| @ApiOperation(value = "적법성 검사 후 다음단계로 넘어가기", notes = "적법성 검사 후 AI 메세지를 보낸다") | |
| @ApiOperation(value = "[적법성 검사] 채팅 5 | 임차인 거절", notes = "임차인 : 적법성 검사 거절") | |
| ResponseEntity<ApiResponse<String>> rejectBuyerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") | |
| ResponseEntity<ApiResponse<Void>> sendStep4( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // 메서드만 만들어서 써도 될 듯 | |
| @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경") | |
| ResponseEntity<ApiResponse<Void>> updateSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody SpecialContractUpdateDTO dto); | |
| public interface ContractController { | |
| // 대기 | |
| @ApiOperation( | |
| value = "[대기] 계약전_임차인 | 계약서를 몽고DB에 저장", | |
| notes = "계약서에 필요한 항목들을 가져와서 몽고 DB에 계약서 만들기") | |
| ResponseEntity<ApiResponse<Void>> saveContractMongo( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // 정보 조회 | |
| @ApiOperation(value = "[정보 조회] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[정보 조회] 채팅 1 | 정보 조회 시작", notes = "정보조회 마지막 단계에서 다음 단계로 넘어가기 Message") | |
| ResponseEntity<ApiResponse<Void>> getContractNext( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation( | |
| value = "[정보 조회] 채팅 2 | 정보 조회에서 다음단계로 가기", | |
| notes = "다음 단계 여부(true/false)를 받아서 다음 단계로 넘어가기") | |
| ResponseEntity<ApiResponse<Boolean>> nextStep( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody NextStepDTO dto); | |
| // 금액 조정 | |
| @ApiOperation(value = "[금액 조정] 채팅 1 | 금액 조회", notes = "금액을 조율하기 위해 금액을 조회") | |
| ResponseEntity<ApiResponse<PaymentDTO>> getDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[금액 조정] 채팅 2 | 금액 요청 ", notes = "임대인이 금액을 요청") | |
| ResponseEntity<ApiResponse<Void>> saveDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody PaymentDTO dto); | |
| @ApiOperation(value = "[금액 조정] 채팅 3 | 금액 거절", notes = "임차인이 금액을 거절") | |
| ResponseEntity<ApiResponse<Void>> deleteDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[금액 조정] 채팅 4 | 금액 수락", notes = "임대인과 임차인 모두 동의") | |
| ResponseEntity<ApiResponse<Void>> updateDepositPrice( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // 적법성 검사 | |
| @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 계약서 1 몽고DB만 있는 것 ", notes = "몽고DB에 특약 저장하기 ") | |
| ResponseEntity<ApiResponse<Void>> saveSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation( | |
| value = "[적법성 검사] 채팅 1 | AI 적법성 확인 from 몽고DB ", | |
| notes = "몽고DB에 있는 계약서를 AI로 보내고, 적법성 받기") | |
| ResponseEntity<ApiResponse<LegalityDTO>> getLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 채팅 2 | 임대인 삭제", notes = "임대인 : 적법성 검사 삭제") | |
| ResponseEntity<ApiResponse<String>> deleteOwnerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 채팅 3 | 임대인 수정", notes = "임대인 : 적법성 검사 수정") | |
| ResponseEntity<ApiResponse<Void>> updateOwnerLegality( | |
| @io.swagger.annotations.ApiParam(value = "채팅방 ID", required = true) @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @io.swagger.annotations.ApiParam(value = "적법성 업데이트 정보", required = true) @RequestBody UpdateLegalityDTO dto); | |
| @ApiOperation(value = "[적법성 검사] 채팅 4 | 임차인 수정", notes = "임차인 : 적법성 검사 수정 완료") | |
| ResponseEntity<ApiResponse<Void>> updateBuyerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody SpecialContractUpdateDTO dto); | |
| @ApiOperation(value = "[적법성 검사] 채팅 5 | 임차인 거절", notes = "임차인 : 적법성 검사 거절") | |
| ResponseEntity<ApiResponse<String>> rejectBuyerLegality( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") | |
| ResponseEntity<ApiResponse<Void>> sendStep4( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| // 메서드만 만들어서 써도 될 듯 | |
| @ApiOperation(value = "바뀐 특약 수정 from 몽고DB", notes = "적법성 검사 후 수정된 특약으로 변경") | |
| ResponseEntity<ApiResponse<Void>> updateSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails, | |
| @RequestBody SpecialContractUpdateDTO dto); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/controller/ContractController.java
around lines 15 to 116, add missing @ApiParam annotations for all controller
method parameters: annotate the @PathVariable Long contractChatId with
@ApiParam(value="contractChatId", required=true, example="123"), mark the
@AuthenticationPrincipal CustomUserDetails userDetails as @ApiParam(hidden=true)
(or equivalent to hide from docs), and add @ApiParam(value="request body",
required=true) to each @RequestBody DTO parameter; import
io.swagger.annotations.ApiParam and apply consistently to every method signature
in this file.
| @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | ||
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); |
There was a problem hiding this comment.
모호한 API 설명 수정 필요
@ApiOperation의 value가 "??? 몽고 디비랑 합친것"으로 시작하는데, 이는 명확하지 않은 설명입니다. API 문서화를 위해 명확한 설명으로 수정이 필요합니다.
- @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기")
+ @ApiOperation(value = "[적법성 검사] 계약서 1 | 계약서 전체 조회 (신원확인 정보 포함)", notes = "MongoDB 계약서와 신원확인 정보를 통합하여 조회")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); | |
| @ApiOperation(value = "[적법성 검사] 계약서 1 | 계약서 전체 조회 (신원확인 정보 포함)", notes = "MongoDB 계약서와 신원확인 정보를 통합하여 조회") | |
| ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails); |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/controller/ContractController.java
around lines 67 to 70, the @ApiOperation value is unclear ("??? 몽고 디비랑 합친것 ...")
and should be replaced with a concise, meaningful description; change the value
to a clear English or Korean phrase that describes this endpoint (e.g.,
"Retrieve contract(s) by contract chat ID" or the Korean equivalent) and update
the notes or add more detail if needed so API docs accurately reflect that this
returns a single contract or all contracts for a given contractChatId and
requires authentication.
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails); | ||
|
|
||
| @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") |
There was a problem hiding this comment.
오타 수정 필요
@ApiOperation value에 "적법성 검사}" 형태로 괄호가 일치하지 않습니다.
- @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기")
+ @ApiOperation(value = "[적법성 검사] 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기")📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") | |
| @ApiOperation(value = "[적법성 검사] 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/controller/ContractController.java
around line 106, the @ApiOperation value contains a mismatched bracket "[적법성
검사}" — replace the closing curly brace with a closing square bracket so the
string becomes "[적법성 검사]" (or otherwise ensure the opening and closing bracket
types match) and keep the rest of the annotation text unchanged.
| @Override | ||
| @PostMapping("/getContracts") | ||
| public ResponseEntity<ApiResponse<ContractDTO>> getContracts( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||
| return ResponseEntity.ok( | ||
| ApiResponse.success(service.getContract(contractChatId, userDetails.getUserId()))); | ||
| } | ||
|
|
There was a problem hiding this comment.
getContracts 엔드포인트가 잘못된 서비스 메서드를 호출합니다
/getContracts는 서비스의 getContracts(...)를 호출해야 하는데, 현재 getContract(...)를 호출하고 있습니다. 즉시 수정 필요합니다.
적용 제안(diff):
- @PostMapping("/getContracts")
+ @PostMapping("/getContracts")
public ResponseEntity<ApiResponse<ContractDTO>> getContracts(
@PathVariable Long contractChatId,
@AuthenticationPrincipal CustomUserDetails userDetails) {
return ResponseEntity.ok(
- ApiResponse.success(service.getContract(contractChatId, userDetails.getUserId())));
+ ApiResponse.success(service.getContracts(contractChatId, userDetails.getUserId())));
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| @PostMapping("/getContracts") | |
| public ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails) { | |
| return ResponseEntity.ok( | |
| ApiResponse.success(service.getContract(contractChatId, userDetails.getUserId()))); | |
| } | |
| @Override | |
| @PostMapping("/getContracts") | |
| public ResponseEntity<ApiResponse<ContractDTO>> getContracts( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails) { | |
| return ResponseEntity.ok( | |
| ApiResponse.success(service.getContracts(contractChatId, userDetails.getUserId()))); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
around lines 103 to 111, the POST /getContracts handler is calling the wrong
service method (service.getContract) — update the call to
service.getContracts(contractChatId, userDetails.getUserId()) so the controller
invokes the plural service method that matches the endpoint; keep the method
signature and return type unchanged and import/resolve any compile errors if the
service method name differs.
| @Override | ||
| @PostMapping("/legalitysss") | ||
| public ResponseEntity<ApiResponse<Void>> saveSpecialContract( | ||
| @PathVariable Long contractChatId, | ||
| @AuthenticationPrincipal CustomUserDetails userDetails) { | ||
| return ResponseEntity.ok( | ||
| ApiResponse.success( | ||
| service.saveSpecialContract(contractChatId, userDetails.getUserId()))); | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
경로 /legalitysss는 오타/임시 경로로 보입니다 — 의미있는 리소스 경로로 교체하세요
/legalitysss는 의미가 불명확하고 유지보수성을 저해합니다. 기존 특약 리소스 규칙과 맞추어 /specialContract로 POST를 노출하는 것이 일관적입니다(동일 경로에 GET/PATCH 이미 존재).
적용 제안(diff):
- @PostMapping("/legalitysss")
+ @PostMapping("/specialContract")
public ResponseEntity<ApiResponse<Void>> saveSpecialContract(📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| @PostMapping("/legalitysss") | |
| public ResponseEntity<ApiResponse<Void>> saveSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails) { | |
| return ResponseEntity.ok( | |
| ApiResponse.success( | |
| service.saveSpecialContract(contractChatId, userDetails.getUserId()))); | |
| } | |
| @Override | |
| @PostMapping("/specialContract") | |
| public ResponseEntity<ApiResponse<Void>> saveSpecialContract( | |
| @PathVariable Long contractChatId, | |
| @AuthenticationPrincipal CustomUserDetails userDetails) { | |
| return ResponseEntity.ok( | |
| ApiResponse.success( | |
| service.saveSpecialContract(contractChatId, userDetails.getUserId()))); | |
| } |
🤖 Prompt for AI Agents
In
src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
around lines 112 to 120, replace the accidental path "/legalitysss" with a
meaningful, consistent resource path used by the other endpoints (e.g.
"/specialContract") and include the contractChatId path variable in the mapping
("/specialContract/{contractChatId}") so the existing @PathVariable Long
contractChatId matches the URL; keep this as a POST mapping to align with
existing GET/PATCH on the same resource and update any related comments/JavaDoc
to reflect the new path.
|
|
||
| // 적법성 검사 | ||
| @Override | ||
| public ContractDTO getContracts (Long contractChatId, Long userId){ | ||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
|
|
||
| ContractDTO dto; | ||
|
|
||
| // 몽고 DB에서 특약부분을 받아서 저장한다. | ||
| try { | ||
| repository.saveSpecialContract(contractChatId); | ||
|
|
||
| ContractMongoDocument document = repository.getContract(contractChatId); | ||
| if (document == null) { | ||
| throw new BusinessException(ContractException.CONTRACT_GET); | ||
| } | ||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
| Long buyerContractId = contractMapper.getBuyerId(contractChatId); | ||
| IdentityVerificationInfoVO ownerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, ownerContractId); | ||
| IdentityVerificationInfoVO buyerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, buyerContractId); | ||
|
|
||
|
|
||
| // 찾은 값을 Dto에 넣고 반환하기 | ||
| dto = ContractDTO.toDTO(document, ownerVO, buyerVO); | ||
| } catch (Exception e) { | ||
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | ||
| log.error("특약사항 저장 실패 ❌", e); | ||
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | ||
| } | ||
|
|
||
| // ContractMongoDocument document = repository.getContract(contractChatId); | ||
| // if (document == null) { | ||
| // throw new BusinessException(ContractException.CONTRACT_GET); | ||
| // } | ||
| // | ||
| // // 찾은 값을 Dto에 넣고 반환하기 | ||
| // ContractDTO dto = ContractDTO.toDTO(document); | ||
|
|
||
| // ContractDTO dto = getContract(contractChatId, userId); | ||
|
|
||
| return dto; | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
getContracts의 부작용(특약 저장) + 중복 로직: 책임 분리 필요
getContracts가 조회 전에 repository.saveSpecialContract(...)를 호출하며, 이어서 getContract와 유사한 DTO 변환 로직을 중복 수행합니다. 조회 메서드는 부작용 없이 동작해야 하며, 공통 로직은 재사용하는 것이 바람직합니다.
리팩터링 제안(diff): 특약 저장을 유지해야 한다면 저장 후 getContract(...)를 재사용하여 중복 제거하세요. 저장이 불필요하다면 저장 호출 자체를 제거하십시오.
- public ContractDTO getContracts (Long contractChatId, Long userId){
- // userId 검증
- validateUserId(contractChatId, userId);
-
- ContractDTO dto;
-
- // 몽고 DB에서 특약부분을 받아서 저장한다.
- try {
- repository.saveSpecialContract(contractChatId);
-
- ContractMongoDocument document = repository.getContract(contractChatId);
- if (document == null) {
- throw new BusinessException(ContractException.CONTRACT_GET);
- }
- Long ownerContractId = contractMapper.getOwnerId(contractChatId);
- Long buyerContractId = contractMapper.getBuyerId(contractChatId);
- IdentityVerificationInfoVO ownerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, ownerContractId);
- IdentityVerificationInfoVO buyerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, buyerContractId);
-
- // 찾은 값을 Dto에 넣고 반환하기
- dto = ContractDTO.toDTO(document, ownerVO, buyerVO);
- } catch (Exception e) {
- // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리
- log.error("특약사항 저장 실패 ❌", e);
- throw new BusinessException(ContractException.CONTRACT_INSERT, e);
- }
-
- return dto;
- }
+ public ContractDTO getContracts(Long contractChatId, Long userId) {
+ validateUserId(contractChatId, userId);
+ try {
+ // 부작용이 반드시 필요하다면 유지, 아니라면 이 줄을 제거
+ repository.saveSpecialContract(contractChatId);
+ } catch (Exception e) {
+ log.error("특약사항 저장 실패 ❌", e);
+ throw new BusinessException(ContractException.CONTRACT_INSERT, e);
+ }
+ // 기존 조회 로직 재사용
+ return getContract(contractChatId, userId);
+ }추가로, 저장 실패 시 사용하는 에러 코드는 CONTRACT_INSERT가 맞으나, 조회 실패는 CONTRACT_GET을 사용하도록 분리되어야 합니다(현재 혼용).
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 적법성 검사 | |
| @Override | |
| public ContractDTO getContracts (Long contractChatId, Long userId){ | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| ContractDTO dto; | |
| // 몽고 DB에서 특약부분을 받아서 저장한다. | |
| try { | |
| repository.saveSpecialContract(contractChatId); | |
| ContractMongoDocument document = repository.getContract(contractChatId); | |
| if (document == null) { | |
| throw new BusinessException(ContractException.CONTRACT_GET); | |
| } | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| Long buyerContractId = contractMapper.getBuyerId(contractChatId); | |
| IdentityVerificationInfoVO ownerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, ownerContractId); | |
| IdentityVerificationInfoVO buyerVO = identityVerificationService.getDecryptedVerificationInfo(contractChatId, buyerContractId); | |
| // 찾은 값을 Dto에 넣고 반환하기 | |
| dto = ContractDTO.toDTO(document, ownerVO, buyerVO); | |
| } catch (Exception e) { | |
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | |
| log.error("특약사항 저장 실패 ❌", e); | |
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | |
| } | |
| // ContractMongoDocument document = repository.getContract(contractChatId); | |
| // if (document == null) { | |
| // throw new BusinessException(ContractException.CONTRACT_GET); | |
| // } | |
| // | |
| // // 찾은 값을 Dto에 넣고 반환하기 | |
| // ContractDTO dto = ContractDTO.toDTO(document); | |
| // ContractDTO dto = getContract(contractChatId, userId); | |
| return dto; | |
| } | |
| // 적법성 검사 | |
| @Override | |
| public ContractDTO getContracts(Long contractChatId, Long userId) { | |
| validateUserId(contractChatId, userId); | |
| try { | |
| // 부작용이 반드시 필요하다면 유지, 아니라면 이 줄을 제거 | |
| repository.saveSpecialContract(contractChatId); | |
| } catch (Exception e) { | |
| log.error("특약사항 저장 실패 ❌", e); | |
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | |
| } | |
| // 기존 조회 로직 재사용 | |
| return getContract(contractChatId, userId); | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 338-381, getContracts currently performs a side-effect
repository.saveSpecialContract(...) and duplicates the retrieval/DTO conversion
logic: remove the side-effect from this read method or, if the save must remain,
call the existing getContract(...) helper after save to reuse retrieval/DTO
mapping; separate error handling so save failures throw BusinessException with
ContractException.CONTRACT_INSERT and retrieval failures throw
ContractException.CONTRACT_GET, and ensure dto is always initialized from the
reused getContract call (or returned directly) to avoid duplicated code and
ambiguous exception mapping.
| // 임대인 삭제 | ||
| @Override | ||
| @Transactional | ||
| public String deleteOwnerLegality(Long contractChatId, Long userId) { | ||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
|
|
||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
|
|
||
| String redisKey = | ||
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | ||
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | ||
| if (valueDataJson == null) { | ||
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | ||
| } | ||
|
|
||
| try { | ||
| stringRedisTemplate.delete(redisKey); | ||
| // contractChatService.AiMessage(contractChatId, "임대인"); | ||
| } catch (Exception e) { | ||
| log.error("수정 요청 응답 처리 실패", e); | ||
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | ||
| } | ||
|
|
||
|
|
||
| return "임대인이 적법성 검사를 삭제했습니다."; | ||
| } | ||
|
|
There was a problem hiding this comment.
🛠️ Refactor suggestion
임대인 전용 권한 검증 누락 + 예외 타입 일관성
- 임대인만 삭제 가능해야 하나
validateUserId로 소유자/임차인 모두 통과할 수 있습니다.validateIsOwner로 교체 필요. IllegalArgumentException,RuntimeException은 글로벌 예외 처리와 일관성이 떨어집니다.BusinessException으로 통일하세요.
적용 제안(diff):
- // userId 검증
- validateUserId(contractChatId, userId);
+ // 임대인 권한 검증
+ validateIsOwner(contractChatId, userId);
...
- if (valueDataJson == null) {
- throw new IllegalArgumentException("대기중인 수정 요청이 없습니다.");
- }
+ if (valueDataJson == null) {
+ throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다.");
+ }
...
- } catch (Exception e) {
- log.error("수정 요청 응답 처리 실패", e);
- throw new RuntimeException("응답 처리 중 오류가 발생했습니다.");
- }
+ } catch (Exception e) {
+ log.error("수정 요청 응답 처리 실패", e);
+ throw new BusinessException(ContractException.CONTRACT_REDIS, e);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 임대인 삭제 | |
| @Override | |
| @Transactional | |
| public String deleteOwnerLegality(Long contractChatId, Long userId) { | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| String redisKey = | |
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (valueDataJson == null) { | |
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | |
| } | |
| try { | |
| stringRedisTemplate.delete(redisKey); | |
| // contractChatService.AiMessage(contractChatId, "임대인"); | |
| } catch (Exception e) { | |
| log.error("수정 요청 응답 처리 실패", e); | |
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | |
| } | |
| return "임대인이 적법성 검사를 삭제했습니다."; | |
| } | |
| // 임대인 삭제 | |
| @Override | |
| @Transactional | |
| public String deleteOwnerLegality(Long contractChatId, Long userId) { | |
| // 임대인 권한 검증 | |
| validateIsOwner(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| String redisKey = | |
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (valueDataJson == null) { | |
| throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다."); | |
| } | |
| try { | |
| stringRedisTemplate.delete(redisKey); | |
| // contractChatService.AiMessage(contractChatId, "임대인"); | |
| } catch (Exception e) { | |
| log.error("수정 요청 응답 처리 실패", e); | |
| throw new BusinessException(ContractException.CONTRACT_REDIS, e); | |
| } | |
| return "임대인이 적법성 검사를 삭제했습니다."; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 454 to 481, the method deleteOwnerLegality currently allows both
owner and tenant due to validateUserId and throws inconsistent exception types;
replace the user check by calling validateIsOwner(contractChatId, userId) so
only the owner can proceed, and change thrown exceptions (the case when redis
key is null and the catch block) to throw a unified BusinessException with
appropriate messages (e.g., "대기중인 수정 요청이 없습니다." and "응답 처리 중 오류가 발생했습니다.") and
include the original exception as the cause in the catch branch for debugging.
| @Override | ||
| @Transactional | ||
| public Void updateOwnerLegality(Long contractChatId, Long userId, UpdateLegalityDTO updateLegalityDTO) { | ||
|
|
||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
|
|
||
| // 몽고 DB에서 특약부분을 받아서 저장한다. | ||
| String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; | ||
|
|
||
| String existingRequest = stringRedisTemplate.opsForValue().get(redisKey); | ||
| if (existingRequest != null) { | ||
| throw new IllegalArgumentException("해당 조항에 대한 수정 요청이 이미 대기중입니다."); | ||
| } | ||
|
|
||
| LegalityRequestDTO requestData = | ||
| LegalityRequestDTO.builder() | ||
| .legalBasis(updateLegalityDTO.getLegalBasis()) | ||
| .requestId(userId) | ||
| .createdAt(LocalDateTime.now().toString()) | ||
| .build(); | ||
| try { | ||
| repository.saveSpecialContract(contractChatId); | ||
| } catch (Exception e) { | ||
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | ||
| log.error("특약사항 저장 실패 ❌", e); | ||
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | ||
| String jsonData = objectMapper.writeValueAsString(requestData); | ||
| // Store as valid JSON for correct parsing later | ||
| String valueData = String.format("{\"requestData\":%s}", jsonData); | ||
| stringRedisTemplate.opsForValue().set(redisKey, valueData); | ||
|
|
||
| contractChatService.AiMessage(contractChatId, "임대인이 적법성 검사 수정을 요청합니다."); | ||
| } catch (Exception e) { | ||
| log.error("수정 요청 저장 실패", e); | ||
| throw new RuntimeException("수정 요청 저장 중 오류가 발생했습니다."); | ||
| } | ||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
임대인 수정 요청: 권한 검증/중복 방지/키 보존기간 설정
- 임대인 권한만 허용해야 합니다(
validateIsOwner). - 중복 요청 예외도
BusinessException으로 통일. - Redis 키에 TTL을 두어 방치 데이터 최소화(이미
java.time.Durationimport 존재).
적용 제안(diff):
- // userId 검증
- validateUserId(contractChatId, userId);
+ // 임대인 권한 검증
+ validateIsOwner(contractChatId, userId);
...
- if (existingRequest != null) {
- throw new IllegalArgumentException("해당 조항에 대한 수정 요청이 이미 대기중입니다.");
- }
+ if (existingRequest != null) {
+ throw new BusinessException(ContractException.CONTRACT_REDIS, "해당 조항에 대한 수정 요청이 이미 대기중입니다.");
+ }
...
- stringRedisTemplate.opsForValue().set(redisKey, valueData);
+ // 48시간 TTL 부여
+ stringRedisTemplate.opsForValue().set(redisKey, valueData, Duration.ofHours(48));📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Override | |
| @Transactional | |
| public Void updateOwnerLegality(Long contractChatId, Long userId, UpdateLegalityDTO updateLegalityDTO) { | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| // 몽고 DB에서 특약부분을 받아서 저장한다. | |
| String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String existingRequest = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (existingRequest != null) { | |
| throw new IllegalArgumentException("해당 조항에 대한 수정 요청이 이미 대기중입니다."); | |
| } | |
| LegalityRequestDTO requestData = | |
| LegalityRequestDTO.builder() | |
| .legalBasis(updateLegalityDTO.getLegalBasis()) | |
| .requestId(userId) | |
| .createdAt(LocalDateTime.now().toString()) | |
| .build(); | |
| try { | |
| repository.saveSpecialContract(contractChatId); | |
| } catch (Exception e) { | |
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | |
| log.error("특약사항 저장 실패 ❌", e); | |
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | |
| String jsonData = objectMapper.writeValueAsString(requestData); | |
| // Store as valid JSON for correct parsing later | |
| String valueData = String.format("{\"requestData\":%s}", jsonData); | |
| stringRedisTemplate.opsForValue().set(redisKey, valueData); | |
| contractChatService.AiMessage(contractChatId, "임대인이 적법성 검사 수정을 요청합니다."); | |
| } catch (Exception e) { | |
| log.error("수정 요청 저장 실패", e); | |
| throw new RuntimeException("수정 요청 저장 중 오류가 발생했습니다."); | |
| } | |
| return null; | |
| } | |
| @Override | |
| @Transactional | |
| public Void updateOwnerLegality(Long contractChatId, Long userId, UpdateLegalityDTO updateLegalityDTO) { | |
| // 임대인 권한 검증 | |
| validateIsOwner(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String existingRequest = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (existingRequest != null) { | |
| throw new BusinessException(ContractException.CONTRACT_REDIS, "해당 조항에 대한 수정 요청이 이미 대기중입니다."); | |
| } | |
| LegalityRequestDTO requestData = | |
| LegalityRequestDTO.builder() | |
| .legalBasis(updateLegalityDTO.getLegalBasis()) | |
| .requestId(userId) | |
| .createdAt(LocalDateTime.now().toString()) | |
| .build(); | |
| try { | |
| String jsonData = objectMapper.writeValueAsString(requestData); | |
| // Store as valid JSON for correct parsing later | |
| String valueData = String.format("{\"requestData\":%s}", jsonData); | |
| // 48시간 TTL 부여 | |
| stringRedisTemplate.opsForValue().set(redisKey, valueData, Duration.ofHours(48)); | |
| contractChatService.AiMessage(contractChatId, "임대인이 적법성 검사 수정을 요청합니다."); | |
| } catch (Exception e) { | |
| log.error("수정 요청 저장 실패", e); | |
| throw new RuntimeException("수정 요청 저장 중 오류가 발생했습니다."); | |
| } | |
| return null; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 483 to 517, enforce owner-only access, unify duplicate-request
errors to BusinessException, and add a TTL when setting the Redis key: call
validateIsOwner(contractChatId, userId) (instead of only validateUserId) before
creating the request; replace the IllegalArgumentException thrown on
existingRequest != null with a BusinessException carrying the appropriate error
code/message; when storing the key use the opsForValue().set(redisKey,
valueData, Duration.ofMinutes(30)) (or a configurable Duration) so the Redis
entry expires; keep the current JSON wrapping and existing try/catch behavior,
but ensure the BusinessException import is used and adjust any tests or callers
accordingly.
| // 임차인 수정 | ||
| @Override | ||
| @Transactional | ||
| public Void updateBuyerLegality(Long contractChatId, Long userId, SpecialContractUpdateDTO dto) { | ||
|
|
||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
|
|
||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
|
|
||
| String redisKey = | ||
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | ||
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | ||
| if (valueDataJson == null) { | ||
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | ||
| } | ||
|
|
||
| try{ | ||
| // JSON에서 clauseOrder와 requestData 추출 | ||
| com.fasterxml.jackson.databind.JsonNode rootNode = objectMapper.readTree(valueDataJson); | ||
| String requestDataJson = rootNode.get("requestData").toString(); | ||
|
|
||
| LegalityRequestDTO requestData = | ||
| objectMapper.readValue(requestDataJson, LegalityRequestDTO.class); | ||
|
|
||
| // 수정하는 로직 짜기 | ||
| updateSpecialContract(contractChatId, userId, dto); | ||
| String resultMessage; | ||
| resultMessage = | ||
| String.format("적법성 수정 후 레디스에서 삭제되었습니다."); | ||
|
|
||
| stringRedisTemplate.delete(redisKey); | ||
| contractChatService.AiMessage(contractChatId, resultMessage); | ||
|
|
||
| }catch (Exception e) { | ||
| log.error("수정 요청 응답 처리 실패", e); | ||
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | ||
| } | ||
|
|
||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
임차인 전용 권한 검증 누락 + 불필요한 JSON 파싱
- 임차인만 응답 가능해야 합니다. 현재
validateUserId는 임대인도 통과합니다.validateIsBuyer(신규)로 교체하세요. - Redis에서
requestData를 파싱하고 있지만 결과를 사용하지 않습니다. 불필요한 파싱은 제거 가능.
적용 제안(diff):
- // userId 검증
- validateUserId(contractChatId, userId);
+ // 임차인 권한 검증
+ validateIsBuyer(contractChatId, userId);
...
- // JSON에서 clauseOrder와 requestData 추출
- com.fasterxml.jackson.databind.JsonNode rootNode = objectMapper.readTree(valueDataJson);
- String requestDataJson = rootNode.get("requestData").toString();
-
- LegalityRequestDTO requestData =
- objectMapper.readValue(requestDataJson, LegalityRequestDTO.class);
+ // (옵션) 필요한 경우에만 requestData 파싱신규 메서드(클래스 내부에 추가 필요):
// 임차인 권한 검증
private void validateIsBuyer(Long contractChatId, Long userId) {
Long buyerId = tenantMapper
.selectContractBuyerId(contractChatId)
.orElseThrow(() -> new BusinessException(PreContractErrorCode.TENANT_USER));
if (!userId.equals(buyerId)) {
throw new BusinessException(PreContractErrorCode.TENANT_USER);
}
}🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 519 to 560, replace the broad validateUserId check with a
buyer-only check and remove the unused JSON parsing: call a new private
validateIsBuyer(contractChatId, userId) (implement it in this class using
tenantMapper.selectContractBuyerId(...).orElseThrow(() -> new
BusinessException(PreContractErrorCode.TENANT_USER)) and compare IDs, throwing
BusinessException if not equal), delete the code that reads/parses requestData
from Redis (remove objectMapper.readTree(...) and objectMapper.readValue(...)
and the unused requestData/requestDataJson variables), and keep the Redis
existence check, the updateSpecialContract call, deletion of the Redis key, and
the AiMessage call inside the try/catch; ensure exceptions still log and rethrow
appropriately.
| // 임차인 거절 | ||
| @Override | ||
| @Transactional | ||
| public String rejectBuyerLegality(Long contractChatId, Long userId) { | ||
|
|
||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
|
|
||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
|
|
||
| String redisKey = | ||
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | ||
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | ||
| if (valueDataJson == null) { | ||
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | ||
| } | ||
|
|
||
| try { | ||
| // contractChatService.AiMessage(contractChatId, "임대인이 적법성 수정을 거절했습니다."); | ||
| } catch (Exception e) { | ||
| log.error("수정 요청 응답 처리 실패", e); | ||
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | ||
| } | ||
|
|
||
| return "임대인이 적법성 수정을 거절했습니다."; | ||
| } | ||
|
|
There was a problem hiding this comment.
거절 처리: 권한 검증/키 삭제/메시지 문구 오류
- 임차인만 거절할 수 있어야 합니다.
validateIsBuyer사용. - 거절 시 Redis 요청을 삭제하지 않아 대기 상태가 지속됩니다. 삭제 필요.
- 반환/안내 문구가 “임대인”으로 되어 있는데, 메서드 의미상 “임차인”이어야 합니다.
적용 제안(diff):
- // userId 검증
- validateUserId(contractChatId, userId);
+ // 임차인 권한 검증
+ validateIsBuyer(contractChatId, userId);
...
- if (valueDataJson == null) {
- throw new IllegalArgumentException("대기중인 수정 요청이 없습니다.");
- }
+ if (valueDataJson == null) {
+ throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다.");
+ }
...
- try {
-// contractChatService.AiMessage(contractChatId, "임대인이 적법성 수정을 거절했습니다.");
- } catch (Exception e) {
- log.error("수정 요청 응답 처리 실패", e);
- throw new RuntimeException("응답 처리 중 오류가 발생했습니다.");
- }
-
- return "임대인이 적법성 수정을 거절했습니다.";
+ try {
+ stringRedisTemplate.delete(redisKey);
+ contractChatService.AiMessage(contractChatId, "임차인이 적법성 수정을 거절했습니다.");
+ } catch (Exception e) {
+ log.error("수정 요청 응답 처리 실패", e);
+ throw new BusinessException(ContractException.CONTRACT_REDIS, e);
+ }
+
+ return "임차인이 적법성 수정을 거절했습니다.";📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 임차인 거절 | |
| @Override | |
| @Transactional | |
| public String rejectBuyerLegality(Long contractChatId, Long userId) { | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| String redisKey = | |
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (valueDataJson == null) { | |
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | |
| } | |
| try { | |
| // contractChatService.AiMessage(contractChatId, "임대인이 적법성 수정을 거절했습니다."); | |
| } catch (Exception e) { | |
| log.error("수정 요청 응답 처리 실패", e); | |
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | |
| } | |
| return "임대인이 적법성 수정을 거절했습니다."; | |
| } | |
| // 임차인 거절 | |
| @Override | |
| @Transactional | |
| public String rejectBuyerLegality(Long contractChatId, Long userId) { | |
| // 임차인 권한 검증 | |
| validateIsBuyer(contractChatId, userId); | |
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | |
| String redisKey = | |
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | |
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | |
| if (valueDataJson == null) { | |
| throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다."); | |
| } | |
| try { | |
| stringRedisTemplate.delete(redisKey); | |
| contractChatService.AiMessage(contractChatId, "임차인이 적법성 수정을 거절했습니다."); | |
| } catch (Exception e) { | |
| log.error("수정 요청 응답 처리 실패", e); | |
| throw new BusinessException(ContractException.CONTRACT_REDIS, e); | |
| } | |
| return "임차인이 적법성 수정을 거절했습니다."; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 562 to 588, the rejectBuyerLegality method currently lacks
buyer-only authorization, does not remove the Redis pending request, and returns
the wrong actor in its message; update the method to: call
validateIsBuyer(contractChatId, userId) instead of or in addition to
validateUserId to ensure only the tenant can reject, delete the Redis key
(stringRedisTemplate.delete(redisKey)) after handling the rejection so the
pending state is cleared, and change the returned and logged message text from
“임대인” to “임차인” so it correctly states that the tenant has rejected the legality
modification.
There was a problem hiding this comment.
Actionable comments posted: 2
♻️ Duplicate comments (6)
src/main/java/org/scoula/domain/contract/service/ContractService.java (1)
95-136: 중복된 JavaDoc 설명 수정 필요여러 메서드에서 동일한 JavaDoc 설명("계약서를 AI로 보내고, 적법성 받기")이 사용되고 있습니다. 각 메서드의 실제 기능에 맞게 설명을 수정해야 합니다.
/** - * step4 적법성 검사 후 수정된 특약으로 변경 + * step4 적법성 검사 후 수정된 특약 저장 * * @param contractChatId 채팅방 아이디 - * @param userId 유저 아이디 @Param dto 변경된 특약 + * @param userId 유저 아이디 + * @param dto 변경된 특약 */ Void updateSpecialContract(Long contractChatId, Long userId, SpecialContractUpdateDTO dto); /** - * step4 (init) 계약서를 AI로 보내고, 적법성 받기 + * step4 임대인의 적법성 요청을 삭제 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 - * @return AI가 계약서를 보고 주는 적법성을 리턴값으로 보내기 + * @return 삭제 완료 메시지 */ String deleteOwnerLegality(Long contractChatId, Long userId); /** - * step4 (init) 계약서를 AI로 보내고, 적법성 받기 + * step4 임대인의 적법성 요청을 업데이트 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 - * @return AI가 계약서를 보고 주는 적법성을 리턴값으로 보내기 + * @param dto 업데이트할 적법성 정보 */ Void updateOwnerLegality(Long contractChatId, Long userId, UpdateLegalityDTO dto); /** - * step4 (init) 계약서를 AI로 보내고, 적법성 받기 + * step4 임차인의 적법성 요청을 업데이트 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 - * @return AI가 계약서를 보고 주는 적법성을 리턴값으로 보내기 + * @param dto 업데이트할 특약 정보 */ Void updateBuyerLegality(Long contractChatId, Long userId, SpecialContractUpdateDTO dto); /** - * step4 (init) 계약서를 AI로 보내고, 적법성 받기 + * step4 임차인의 적법성 요청을 거절 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 - * @return AI가 계약서를 보고 주는 적법성을 리턴값으로 보내기 + * @return 거절 완료 메시지 */ String rejectBuyerLegality(Long contractChatId, Long userId);src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java (1)
165-172: GET 메서드로 상태 변경 작업 수행 - RESTful 원칙 위반
rejectBuyerLegality는 상태를 변경하는 작업인데 GET 메서드를 사용하고 있습니다. POST 또는 DELETE 메서드가 더 적절합니다.@Override - @GetMapping("/reject/legality") + @PostMapping("/reject/legality") public ResponseEntity<ApiResponse<String>> rejectBuyerLegality( @PathVariable Long contractChatId, @AuthenticationPrincipal CustomUserDetails userDetails) { return ResponseEntity.ok( ApiResponse.success( service.rejectBuyerLegality(contractChatId, userDetails.getUserId()))); }src/main/java/org/scoula/domain/contract/controller/ContractController.java (1)
106-106: 괄호 불일치 오타 수정 필요
@ApiOperationvalue에 "[적법성 검사}" 형태로 괄호가 일치하지 않습니다.- @ApiOperation(value = "[적법성 검사} 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기") + @ApiOperation(value = "[적법성 검사] 채팅 6 | 적법성 검사 후 다음단계로 넘어가기", notes = "내보내기 단계로 넘어가기")src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (3)
565-588: 임차인 권한 검증 및 메시지 텍스트 수정 필요거절은 임차인만 할 수 있어야 하며, 반환 메시지가 "임대인"이 아닌 "임차인"이어야 합니다.
@Override @Transactional public String rejectBuyerLegality(Long contractChatId, Long userId) { - // userId 검증 - validateUserId(contractChatId, userId); + // 임차인 권한 검증 + validateIsBuyer(contractChatId, userId); Long ownerContractId = contractMapper.getOwnerId(contractChatId); String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); if (valueDataJson == null) { - throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다."); } try { stringRedisTemplate.delete(redisKey); -// contractChatService.AiMessage(contractChatId, "임대인이 적법성 수정을 거절했습니다."); + contractChatService.AiMessage(contractChatId, "임차인이 적법성 수정을 거절했습니다."); } catch (Exception e) { log.error("수정 요청 응답 처리 실패", e); - throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, e); } - return "임대인이 적법성 수정을 거절했습니다."; + return "임차인이 적법성 수정을 거절했습니다."; }
485-517: 임대인 권한 검증 및 Redis TTL 설정 필요임대인만 수정 요청을 할 수 있도록 권한을 강화하고, Redis 키에 TTL을 설정하여 오래된 데이터가 남지 않도록 해야 합니다.
@Override @Transactional public Void updateOwnerLegality(Long contractChatId, Long userId, UpdateLegalityDTO updateLegalityDTO) { - // userId 검증 - validateUserId(contractChatId, userId); + // 임대인 권한 검증 + validateIsOwner(contractChatId, userId); Long ownerContractId = contractMapper.getOwnerId(contractChatId); String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; String existingRequest = stringRedisTemplate.opsForValue().get(redisKey); if (existingRequest != null) { - throw new IllegalArgumentException("해당 조항에 대한 수정 요청이 이미 대기중입니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, "해당 조항에 대한 수정 요청이 이미 대기중입니다."); } LegalityRequestDTO requestData = LegalityRequestDTO.builder() .legalBasis(updateLegalityDTO.getLegalBasis()) .requestId(userId) .createdAt(LocalDateTime.now().toString()) .build(); try { String jsonData = objectMapper.writeValueAsString(requestData); // Store as valid JSON for correct parsing later String valueData = String.format("{\"requestData\":%s}", jsonData); - stringRedisTemplate.opsForValue().set(redisKey, valueData); + // 48시간 TTL 설정 + stringRedisTemplate.opsForValue().set(redisKey, valueData, Duration.ofHours(48)); contractChatService.AiMessage(contractChatId, "임대인이 적법성 검사 수정을 요청합니다."); } catch (Exception e) { log.error("수정 요청 저장 실패", e); - throw new RuntimeException("수정 요청 저장 중 오류가 발생했습니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, e); } return null; }
457-480: 임대인 권한 검증 및 예외 처리 일관성 개선 필요
deleteOwnerLegality에서 임대인만 삭제할 수 있도록 권한 검증을 강화하고, 예외 타입을 일관되게 사용해야 합니다.@Override @Transactional public String deleteOwnerLegality(Long contractChatId, Long userId) { - // userId 검증 - validateUserId(contractChatId, userId); + // 임대인 권한 검증 + validateIsOwner(contractChatId, userId); Long ownerContractId = contractMapper.getOwnerId(contractChatId); String redisKey = "final-contract:legality:" + contractChatId + ":" + ownerContractId; String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); if (valueDataJson == null) { - throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, "대기중인 수정 요청이 없습니다."); } try { stringRedisTemplate.delete(redisKey); -// contractChatService.AiMessage(contractChatId, "임대인"); + contractChatService.AiMessage(contractChatId, "임대인이 적법성 검사를 삭제했습니다."); } catch (Exception e) { log.error("수정 요청 응답 처리 실패", e); - throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); + throw new BusinessException(ContractException.CONTRACT_REDIS, e); } return "임대인이 적법성 검사를 삭제했습니다."; }
🧹 Nitpick comments (4)
src/main/java/org/scoula/domain/contract/service/ContractService.java (1)
82-82: JavaDoc 형식 오류 수정 필요JavaDoc이 한 줄로 압축되어 있어 가독성이 떨어집니다. 적절한 형식으로 수정해주세요.
- /** * step4 start 특약을 개약 테이블에 저장하기 * * @param contractChatId 채팅방 아이디 * @param userId 유저 아이디 */ + /** + * step4 start 특약을 계약 테이블에 저장하기 + * + * @param contractChatId 채팅방 아이디 + * @param userId 유저 아이디 + */src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java (1)
103-111: 주석 처리된 코드 제거 권장주석 처리된
getContracts메서드는 사용되지 않으며, 서비스 메서드 호출도 잘못되어 있습니다. 필요하지 않다면 제거하는 것이 좋습니다.- // @Override - // @PostMapping("/getContracts") - // public ResponseEntity<ApiResponse<ContractDTO>> getContracts( - // @PathVariable Long contractChatId, - // @AuthenticationPrincipal CustomUserDetails userDetails) { - // return ResponseEntity.ok( - // ApiResponse.success(service.getContract(contractChatId, - // userDetails.getUserId()))); - // }src/main/java/org/scoula/domain/contract/controller/ContractController.java (1)
66-70: 주석 처리된 코드 제거 권장사용하지 않는 주석 처리된
getContracts메서드는 코드베이스를 깔끔하게 유지하기 위해 제거하는 것이 좋습니다.- // // 적법성 검사 - // @ApiOperation(value = "??? 몽고 디비랑 합친것 [적법성 검사] 계약서 1 | 계약서 전체 조회", notes = "계약서 가져오기") - // ResponseEntity<ApiResponse<ContractDTO>> getContracts( - // @PathVariable Long contractChatId, - // @AuthenticationPrincipal CustomUserDetails userDetails);src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (1)
339-380: 사용하지 않는 주석 코드 제거대량의 주석 처리된
getContracts메서드가 있습니다. 사용하지 않는다면 제거하여 코드베이스를 깔끔하게 유지하세요.주석 처리된 전체 메서드를 제거하시기 바랍니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (4)
src/main/java/org/scoula/domain/contract/controller/ContractController.java(1 hunks)src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java(2 hunks)src/main/java/org/scoula/domain/contract/service/ContractService.java(2 hunks)src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java(9 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
src/main/java/org/scoula/**/controller/*Controller{,Impl}.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
Use interface-implementation pattern for controllers:
*Controller.java(interface) and*ControllerImpl.java(implementation)
Files:
src/main/java/org/scoula/domain/contract/controller/ContractController.javasrc/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
src/main/java/org/scoula/**/controller/**/*.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
src/main/java/org/scoula/**/controller/**/*.java: All controller endpoints should return a consistentApiResponse<T>wrapper
Add Swagger annotations to controller endpoints for API documentation
Files:
src/main/java/org/scoula/domain/contract/controller/ContractController.javasrc/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
src/main/java/org/scoula/**/service/*Service{Interface,Impl}.java
📄 CodeRabbit Inference Engine (CLAUDE.md)
Use interface-implementation pattern for services:
*ServiceInterface.java(interface) and*ServiceImpl.java(implementation)
Files:
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
🧠 Learnings (2)
📚 Learning: 2025-08-12T03:59:30.512Z
Learnt from: CR
PR: ITZEEP/backend#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-12T03:59:30.512Z
Learning: Applies to src/main/java/org/scoula/**/controller/**/*.java : Add Swagger annotations to controller endpoints for API documentation
Applied to files:
src/main/java/org/scoula/domain/contract/controller/ContractController.java
📚 Learning: 2025-08-12T03:59:30.512Z
Learnt from: CR
PR: ITZEEP/backend#0
File: CLAUDE.md:0-0
Timestamp: 2025-08-12T03:59:30.512Z
Learning: Applies to src/main/java/org/scoula/**/controller/*Controller{,Impl}.java : Use interface-implementation pattern for controllers: `*Controller.java` (interface) and `*ControllerImpl.java` (implementation)
Applied to files:
src/main/java/org/scoula/domain/contract/controller/ContractControllerImpl.java
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: CodeQL Security Analysis (java)
- GitHub Check: Test Coverage
🔇 Additional comments (1)
src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java (1)
196-197: 주석 처리된 코드 제거 또는 복원 필요스텝 변경 코드가 주석 처리되어 있습니다. 의도적이라면 제거하고, 필요한 코드라면 복원해주세요.
-// // 스텝 변경 -// contractChatMapper.updateStatus(contractChatId, ContractChat.ContractStatus.STEP1); + // 스텝 변경 + contractChatMapper.updateStatus(contractChatId, ContractChat.ContractStatus.STEP1);
| public Void saveSpecialContract(Long contractChatId, Long userId) { | ||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
| // 몽고 DB에서 특약부분을 받아서 저장한다. | ||
| try { repository.saveSpecialContract(contractChatId); } catch (Exception e) { | ||
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | ||
| log.error("특약사항 저장 실패 ❌", e); | ||
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); } | ||
| return null; | ||
| } |
There was a problem hiding this comment.
🛠️ Refactor suggestion
예외 처리 및 포맷팅 개선 필요
특약 저장 로직의 try-catch 블록 포맷팅이 불규칙하고, 예외 메시지가 명확하지 않습니다.
@Override
public Void saveSpecialContract(Long contractChatId, Long userId) {
- // userId 검증
- validateUserId(contractChatId, userId);
- // 몽고 DB에서 특약부분을 받아서 저장한다.
- try { repository.saveSpecialContract(contractChatId); } catch (Exception e) {
- // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리
- log.error("특약사항 저장 실패 ❌", e);
- throw new BusinessException(ContractException.CONTRACT_INSERT, e); }
- return null;
+ // userId 검증
+ validateUserId(contractChatId, userId);
+
+ // 몽고 DB에서 특약부분을 받아서 저장한다.
+ try {
+ repository.saveSpecialContract(contractChatId);
+ } catch (Exception e) {
+ log.error("특약사항 저장 실패 - contractChatId: {}", contractChatId, e);
+ throw new BusinessException(ContractException.CONTRACT_INSERT, e);
+ }
+ return null;
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| public Void saveSpecialContract(Long contractChatId, Long userId) { | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| // 몽고 DB에서 특약부분을 받아서 저장한다. | |
| try { repository.saveSpecialContract(contractChatId); } catch (Exception e) { | |
| // 예외 로그 기록 및 사용자에게 전달할 메시지 등 처리 | |
| log.error("특약사항 저장 실패 ❌", e); | |
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); } | |
| return null; | |
| } | |
| @Override | |
| public Void saveSpecialContract(Long contractChatId, Long userId) { | |
| // userId 검증 | |
| validateUserId(contractChatId, userId); | |
| // 몽고 DB에서 특약부분을 받아서 저장한다. | |
| try { | |
| repository.saveSpecialContract(contractChatId); | |
| } catch (Exception e) { | |
| log.error("특약사항 저장 실패 - contractChatId: {}", contractChatId, e); | |
| throw new BusinessException(ContractException.CONTRACT_INSERT, e); | |
| } | |
| return null; | |
| } |
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 383 to 392, the try-catch block is poorly formatted and the
exception log/message is not descriptive; reformat the try-catch to standard
Java indentation, call repository.saveSpecialContract(contractChatId) inside the
try on its own line, and improve error reporting by logging a clear message that
includes contextual identifiers (contractChatId and userId) and the exception
(e.g., log.error("Failed to save special contract for contractChatId={},
userId={}", contractChatId, userId, e)); then throw a BusinessException
constructed with a descriptive message and the caught exception as the cause.
| public Void updateBuyerLegality(Long contractChatId, Long userId, SpecialContractUpdateDTO dto) { | ||
|
|
||
| // userId 검증 | ||
| validateUserId(contractChatId, userId); | ||
|
|
||
| Long ownerContractId = contractMapper.getOwnerId(contractChatId); | ||
|
|
||
| String redisKey = | ||
| "final-contract:legality:" + contractChatId + ":" + ownerContractId; | ||
| String valueDataJson = stringRedisTemplate.opsForValue().get(redisKey); | ||
| if (valueDataJson == null) { | ||
| throw new IllegalArgumentException("대기중인 수정 요청이 없습니다."); | ||
| } | ||
|
|
||
| try{ | ||
| // JSON에서 clauseOrder와 requestData 추출 | ||
| com.fasterxml.jackson.databind.JsonNode rootNode = objectMapper.readTree(valueDataJson); | ||
| String requestDataJson = rootNode.get("requestData").toString(); | ||
|
|
||
| LegalityRequestDTO requestData = | ||
| objectMapper.readValue(requestDataJson, LegalityRequestDTO.class); | ||
|
|
||
| // 수정하는 로직 짜기 | ||
| updateSpecialContract(contractChatId, userId, dto); | ||
| String resultMessage; | ||
| resultMessage = | ||
| String.format("적법성 수정 후 레디스에서 삭제되었습니다."); | ||
|
|
||
| stringRedisTemplate.delete(redisKey); | ||
| contractChatService.AiMessage(contractChatId, resultMessage); | ||
|
|
||
| }catch (Exception e) { | ||
| log.error("수정 요청 응답 처리 실패", e); | ||
| throw new RuntimeException("응답 처리 중 오류가 발생했습니다."); | ||
| } | ||
|
|
||
|
|
||
| return null; | ||
| } |
There was a problem hiding this comment.
임차인 권한 검증 필요
임차인만 수정할 수 있도록 전용 권한 검증이 필요합니다. 현재 validateUserId는 임대인도 통과할 수 있습니다.
임차인 전용 검증 메서드 validateIsBuyer를 추가해야 합니다:
// 임차인 권한 검증
private void validateIsBuyer(Long contractChatId, Long userId) {
Long buyerId = tenantMapper
.selectContractBuyerId(contractChatId)
.orElseThrow(() -> new BusinessException(PreContractErrorCode.TENANT_USER));
if (!userId.equals(buyerId)) {
throw new BusinessException(PreContractErrorCode.TENANT_USER);
}
}그리고 메서드를 다음과 같이 수정하세요:
@Override
@Transactional
public Void updateBuyerLegality(Long contractChatId, Long userId, SpecialContractUpdateDTO dto) {
- // userId 검증
- validateUserId(contractChatId, userId);
+ // 임차인 권한 검증
+ validateIsBuyer(contractChatId, userId);새로운 메서드를 추가하시겠습니까?
🤖 Prompt for AI Agents
In src/main/java/org/scoula/domain/contract/service/ContractServiceImpl.java
around lines 522 to 560, the method updateBuyerLegality currently uses
validateUserId which allows landlords as well; add a new private method
validateIsBuyer that fetches the buyerId via
tenantMapper.selectContractBuyerId(contractChatId) and throws
BusinessException(PreContractErrorCode.TENANT_USER) when absent or when userId
does not equal buyerId, then replace the call to validateUserId(...) at the
start of updateBuyerLegality with validateIsBuyer(...); place the new
validateIsBuyer method in the same class (private) and use the same
BusinessException and error code as in the review comment.
🚀 관련 이슈
🔑 주요 변경사항
✔️ 체크 리스트
mainbranch에 실수로 PR 생성 금지)📢 To Reviewers
📸 스크린샷 or 실행영상
Summary by CodeRabbit
신기능
변경 사항